#!/usr/bin/python
# -*- coding: utf-8 -*-
# Filename : dummyNode.py

import binascii
import random
import p2p
import sys
import signal
import struct
import traceback
import datetime
import M2Crypto
import encryption_util
import os
import time
import commands
from encryption_util import *
from M2Crypto import *
import threading
from xml.dom.minidom import *
from xml.dom import minidom, Node
import xml
from binascii import hexlify, unhexlify
import socket

##################################################################################
########################   Global definitions Section  ###########################
##################################################################################

# CA info
AUTHORITY_ADDR = "139.91.70.38"
AUTHORITY_PORT = 11224

# CA - client
X509REQ = "XREQ"
OCSP = "OCSP"
REGISTER = "REGI"

STATUS = "STATUS"
CALINE = "LINE" #Direct connection to CA
FAIL = "FAIL" #Failure Signal

# Site - Client
RTS = "RTS"  
CTS = "CTS"

DATA = "DATA"
MSG = "MSG"
CMD = "CMD"

FILE = "FILE"
PATH = "PATH"
FILENAME = "FILENAME"

# Default
DEFAULT = "0000"


CERT_DIR="./client_certificate/"
CERT_FILE="./client_certificate/client"
CERT_FILE_CA="./client_certificate/ca"


####################################################################################
########################### UTILITIES ##############################################
####################################################################################

def btdebug( self, msg ):
    """ Prints a messsage to the screen with the name of the current thread """
    print "[%s] %s" % ( str(threading.currentThread().getName()), msg )



def emergencyLine ( ) :

    print "\n------------------------Creating Permanent Connection to Server\n--------------------------------"
    
    line = p2p.BTPeerConnection ( None,  AUTHORITY_ADDR, AUTHORITY_PORT , None , debug=True, file=CERT_FILE )

    return line



####################################################################################
############################## Handlers ############################################      
####################################################################################


#--------------------------------------------------------------------------
def defaultHandler(peerconn, msgdata) :
#--------------------------------------------------------------------------
	print(peerconn.clientInfo(), msgdata)



#--------------------------------------------------------------------------
def recvData ( msgdata ):
#--------------------------------------------------------------------------

	res = p2p.extractData ( msgdata )
	typeMsg = res[0]
	pkt = res[1]

	if typeMsg == MSG :
		print "Received Message ", pkt
		return "Received"

	elif typeMsg == CMD :
		# returning tuple (status, output)
		cmdReply = commands.getstatusoutput( pkt )
		cmdReply = cmdReply[1]
		if cmdReply ==  "":
			cmdReply = "Executed Successfully"
		
		print ("CMD reply : ", cmdReply)
		return cmdReply
	else :
		print "Something gone terribly wrong. Received : ", typeMsg
		return False


# If it's the intermediate node, it forwards message to destination
# then return the reply to server without keeping the results
#--------------------------------------------------------------------------
def requestToSendMsg ( peerconn, msgdata ):
#--------------------------------------------------------------------------
	# Getting Master / Slave Ip and port
	master , slave = p2p.sourceAndDestination ( msgdata )

	# MYID : IP = MYID[0]  , Port = MYID[1]
	MYID = 	peerconn.myInfo()
	print ("Me , My Master, My Slave " , MYID , master, slave )

	if master == MYID :
		print "I 'm master ... i 'm forwarding it'"

		remoteConn = p2p.BTPeerConnection( None, slave[0], slave[1], None , debug=True, file=CERT_FILE )

		remoteConn.senddata( msgdata )	# Sending to destination
                reply =  remoteConn.recvdata()	# Waiting for reply

            	remoteConn.close()

		print ( "Forwarding Reply to Server  ..." , reply )

		peerconn.senddata( reply )	# Sending reply to previous

	elif slave == MYID :
		print "I 'm slave ... i 'm taking orders"
		reply = recvData ( msgdata )
		print "Replying ...."

		res = p2p.createXMLcontent( MYID[0] , MYID[1] , master[0] , master[1] ,  MSG  , str ( reply) )
		peerconn.senddata( res )		

	else :
		print "Wrong Slave Id. Propably an attack occured " , slave 


#--------------------------------------------------------------------------
def recvFile (  filedata ) :
#--------------------------------------------------------------------------

	#print ("Received Message From  : " , peerconn.clientInfo())
	#print ("FILE DATA ", filedata )
	master , slave = p2p.sourceAndDestination ( filedata )
	fileName , asciiData = p2p.extractData ( filedata )

	binData = binascii.a2b_base64 ( asciiData )

	print( "Opening File  : " , fileName , "\t CRC : " , binascii.crc32( binData ) )
	
	f = open( fileName,"wb" )
	f.write( binData )
	f.close()

	return "Tranfer Complete"


#--------------------------------------------------------------------------
def requestToSendFile( peerconn, msgdata ) :
#--------------------------------------------------------------------------
	print "\n--------------- New command received --------------\n"
	# MYID : IP = MYID[0]  , Port = MYID[1]
	MYID = 	peerconn.myInfo()
	master , slave = p2p.sourceAndDestination ( msgdata )
	print (MYID , master, slave )

	if master == MYID :
		res = p2p.extractData ( msgdata )
		typeMsg = res[0]
		path = res[1]

		if os.path.exists(path) and os.path.isfile(path) :

			f = open( path , "rb")
			fileData = f.read()
			f.close()

			asciiData = binascii.b2a_base64 ( fileData )
			print ("Sending " , path ,  "\t CRC : " ,  binascii.crc32( fileData ) )

			toBeSent = p2p.createXMLcontent( master[0] , master[1] , slave[0] , slave[1] ,
			 os.path.basename( path )+"_backup" , asciiData , FILE )
			
			remoteConn = p2p.BTPeerConnection( None, slave[0], slave[1], None , debug=True, file=CERT_FILE )

			remoteConn.senddata( toBeSent )	# Sending to destination
		        reply =  remoteConn.recvdata()	# Waiting for reply
		    	remoteConn.close()

			print ( "Forwarding Reply to Server  ... " )
			peerconn.senddata( reply )
		else :

			toBeSent = p2p.createXMLcontent( MYID[0] , MYID[1] ,"serverIp" , "serverPort" , 
			 ERROR , "No Such File\n" , FILE )
			peerconn.senddata ( toBeSent )
			return False

	elif slave == MYID :
		print "I 'm slave ... i 'm receiving file "
		reply = recvFile ( msgdata )
		print "Replying ...."

		res = p2p.createXMLcontent( MYID[0] , MYID[1] , master[0] , master[1] ,  MSG  , str ( reply) )
		peerconn.senddata( res )	

	else :
		print "Wrong Slave Id. Propably an attack occured " , slave 

# TODO : apply policy for allowance
#--------------------------------------------------------------------------
def allowToConnect(peerconn, msgdata) : 
#--------------------------------------------------------------------------

	print(" Want To Connect To Me : ", msgdata)


	master , slave = p2p.sourceAndDestination ( msgdata )

	toBeSent = p2p.createXMLcontent( master[0] , master[1] , slave[0] , slave[1] ,	 "Allow" , "TRUE" , DATA )

	print ("Sending ", toBeSent )
        peerconn.senddata( toBeSent )

	print "Waiting for the other node to connect"


#--------------------------------------------------------------------------
def requestStatus(peerconn, msgdata) :
#--------------------------------------------------------------------------

    	print("\n\n----------------------- Status Request : ", msgdata)
        print(" \n\n\n -----------------------")

	master , slave = p2p.sourceAndDestination ( msgdata )


        cpuMem = commands.getoutput("top -b -n 2  | head -n 6")
        #disk = commands.getoutput("df -ThP 2> /dev/null | grep -v tmpfs")
        #print "Result : \n %s \n --------------- \n %s \n -------" % ( cpuMem , disk)
        
        #result = str(cpuMem) + str(disk)
        # epeidh to df -h petaei ellhnika sthn konsola ... GAMIETAI
        result = str(cpuMem)

	toBeSent = p2p.createXMLcontent( slave[0] , slave[1] , master[0] , master[1] ,	 "STATUS" , result, STATUS)

	print ("Sending ", toBeSent )
        peerconn.senddata( toBeSent )




####################################################################################     
##################### Class OSCPreq  ###############################################
####################################################################################  
class OCSPreq:

    #--------------------------------------------------------------------------
    def __init__( self, serverport, server, cacert_path, client_key_path, client_cert_path ):
    #--------------------------------------------------------------------------
        self.server = server
	self.serverport = serverport            
	self.cacert_path = cacert_path
	self.client_key_path = client_key_path
	self.client_cert_path = client_cert_path


    def check(self, cert):
        #############################################
        # connect to authority server and send request
        # Connections over SSL
        # If you comment out the next 2 lines, the connection won't be secure
        # FIXME: Verification of the should be from user not for 

	asciiData = cert.as_pem()

	serverConn = p2p.BTPeerConnection(None,  self.server , self.serverport , None , debug=True, file=CERT_FILE )
	serverConn.senddata( asciiData , OCSP)
        
        answer = serverConn.recvdata ()
      
        #ca_cert=c.get_peer_cert()
	print "OCSP Got answer: " + answer 

	return 1





####################################################################################     
##################### Class Node ###################################################
####################################################################################    
class Node:


    def _verifyHostname(self, connection, x509, errno, depth, preverifyOK):
        if preverifyOK:
            if self.hostname == x509.get_subject().commonName:
                return False
        return preverifyOK


    def _certificate_create(self,file,file_ca, ownerEmail):

        # If key not exists 
        Rand.load_file('randpool', -1)
    	pubkey, privkey, ekey = create_rsa_keyring_save(file+".key")

        Rand.save_file('randpool')
        
        # Is used in order to obtain our Ip
        s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
        s.connect( ( "www.google.com", 80 ) )
        myIp = s.getsockname()[0]        
        print "Registration Info .... " , myIp , ownerEmail
        
        # Create a new x.509 request 
	# FIXME: user should enter the values
        cert_name = X509.X509_Name()
        cert_name.C = "GR"
        cert_name.ST = "Crete"
        cert_name.L = "Herakleion"
        cert_name.O = "UoC1"
        cert_name.CN = myIp
        cert_name.OU = "CSD1"
        cert_name.Email = ownerEmail

        # Sign the request         
        request = create_cert_request(ekey, ekey, cert_name, "sha1")
        msgdata=request.as_pem()

	print ( "Sending request to CA " , (AUTHORITY_ADDR,AUTHORITY_PORT) )
	serverConn = p2p.BTPeerConnection(None,  AUTHORITY_ADDR, AUTHORITY_PORT , None , debug=True )
	serverConn.senddata( msgdata , X509REQ )

	cert_text = serverConn.recvdata ()

        # create actual certificate
        cert=X509.load_cert_string(cert_text, 1)
        # now we must check the certificate for signature

	sslConn = serverConn.clientSocket()
        ca_cert= sslConn.get_peer_cert()
        # first the client
        

        if verify_x509_certificate(ca_cert, cert)==1:
            print 'x509 Certificate seems ok!'
        else:
            sys.exit("Failed to verify the returned x509 certificate\n")

        
        cert.save(file+".pem")
        ca_cert.save(file_ca+".pem")

       


    def _load_security(self):
        
        passWord = askForPass()
        if not os.path.exists( CERT_DIR ) :
            os.makedirs(CERT_DIR)

	if not ( os.path.exists(CERT_FILE+".pem") or os.path.exists(CERT_FILE+".pem")):

		print "Keyfile/certificate not found creating new key "
		ownerEmail = raw_input("Give owner's email :")
		self._certificate_create( CERT_FILE, CERT_FILE_CA, ownerEmail )
        # Now check for common name:
 	

 
        cert, privkey, pubkey, ekey, ca_cert =  load_certificate_client(file=CERT_FILE,file_ca=CERT_FILE_CA)

        name = cert.get_subject()
        print "subject common name is : " + name.CN

        if (ca_cert.verify()==1):
            print 'x509 ca certificate passed check from it self (1/2)\n'
        else:
           sys.exit("Failed to verify the x509 certificate (ca_cert)\n")

        # finally check if its signed
        if (verify_x509_certificate(ca_cert,cert)==1):
           print 'x509 certificate passed check from authority (2/2)\n'
        else:
           sys.exit("Failed to verify the x509 certificate (ca-ca_cert) \n")


	ocsp = OCSPreq( AUTHORITY_PORT, AUTHORITY_ADDR, CERT_FILE_CA + ".pem", CERT_FILE + ".pem", CERT_FILE+".key" )
	ocsp.check( cert ) 
       
	passwd = raw_input("Give password for webpage[none to keep current]:")

	if ( passwd != ""):
		print "Sending password : %s to CA" % passwd
		serverConn0 = p2p.BTPeerConnection(None,  AUTHORITY_ADDR, AUTHORITY_PORT , None , debug=True, file=CERT_FILE )
                # FWTH FIXME: an ftia3eis to dummy node den 8a exei provlhma auto
		serverConn0.senddata( name.Email + " " + passwd , REGISTER )
		reply =  serverConn.recvdata()
		if ( reply == 'OK'):
			print "Password updated!\n"
		else:
			print "Failed to update password!\n"
			return False
		serverConn0.close()

 
        return cert, privkey, pubkey,  ekey, ca_cert

	
       
    #--------------------------------------------------------------------------
    def __init__( self, localPort , serverIp = AUTHORITY_ADDR , serverPort = AUTHORITY_PORT, myid = None,  debug = 0):
    #--------------------------------------------------------------------------
	# Initializing
	self.debug = debug
	self.serverIp = serverIp
	self.serverPort = serverPort
	self.peer = p2p.Peer( localPort )
        print ( " I am ", self.peer.myid )

	try :
	    # Security part
            self.cert, self.privkey, self.pubkey,  self.ekey, self.ca_cert  =  self._load_security()
            
            self.caLine = emergencyLine () # Creating Emergency line with CA
            self.caLine.senddata( str(self.peer.myid[1]) + " " + self.cert.get_subject().Email , CALINE) #Sending myPort to CA in order to register it

            self.peer.addhandler( DEFAULT, defaultHandler)
	    self.peer.addhandler( RTS ,  allowToConnect )
	    self.peer.addhandler( DATA , requestToSendMsg )
	    self.peer.addhandler( FILE , requestToSendFile )
            self.peer.addhandler( STATUS, requestStatus )
	    
            # Threading mainloop in order to be able to send emergency
	    t = threading.Thread( target = self.peer.mainloop)
	    t.start()

	except :
            sys.exit("Could Not Connect to CA")

#================= Node -> Server || Node ===============================================================

    #--------------------------------------------------------------------------
    def sendEmergency(self):
    #--------------------------------------------------------------------------
        try :
	    print "\n ---------------- SENDING EMERGENCY SIGNAL --------------\n "
            sended = self.caLine.senddata ( "failure .. we need back up", FAIL )
	    return sended
        except :
            traceback.print_exc()
            self.caline.close()
	    return False



    #--------------------------------------------------------------------------
    def SendtoNode( self, connectTo, port, data , waitreply = True):
    #--------------------------------------------------------------------------
         return self.peer.connectandsend( connectTo, port,  data , waitreply)


#=================================================================================================


####################################################################################
########################### Main ###################################################
####################################################################################
if __name__ == "__main__" :
	
	try :
		print('Connecting to Server ...')
		localPort = int(input('Which localPort ? : '))
		#localPort = 4444
		#serverIp = raw_input("What's server's Ip ?")
		#serverPort = int(input("What's server's Port ?")
		serverIp = AUTHORITY_ADDR
		serverPort = AUTHORITY_PORT


		node = Node( localPort, serverIp, serverPort )

		while ( True ) :
			sel = input ( "1) Emergency Signal \n2)Exit\n")
			if sel == 1 :
				node.sendEmergency()
			elif sel == 2 :
				print "Exiting ...."
				sys.exit(1)
			else :
				print "Wrong Option"		

	except KeyboardInterrupt:
		print 'KeyboardInterrupt: stopping mainloop'
		sys.exit(0)
	except:
		traceback.print_exc()
		sys.exit(0)

    
